package com.ecomm.util;

/**
 * Created by IntelliJ IDEA.
 * User: fjj
 * Date: 2005-3-25
 * Time: 11:23:55
 * To change this template use File | Settings | File Templates.
 */
public final class Base64 {

  private static final char [] BASE64CHARS = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
                        'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
                        'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a',
                        'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
                        'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
                        't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1',
                        '2', '3', '4', '5', '6', '7', '8', '9', '+',
                        '/'};

  private static final char PAD = '=';
  private static final String CRLF = "\r\n";

  private Base64() {}

  public static String encode(String str) {

    StringBuffer output = new StringBuffer();
    byte[] source = str.getBytes();
    int buffer;

    for(int i = 0; i < source.length; i += 3) {

      buffer = 0;

      if((i % 54) == 0 && i != 0)
        output.append(CRLF);

      if(source.length <= i + 1) {

        buffer = (eightbit(source[i]) << 16);
        output.append(BASE64CHARS[sixbit(buffer >> 18)]);
        output.append(BASE64CHARS[sixbit(buffer >> 12)]);
        output.append(PAD);
        output.append(PAD);

      }
      else if(source.length <= i + 2) {

        buffer = (eightbit(source[i]) << 16) | (eightbit(source[i + 1]) << 8);
        output.append(BASE64CHARS[sixbit(buffer >> 18)]);
        output.append(BASE64CHARS[sixbit(buffer >> 12)]);
        output.append(BASE64CHARS[sixbit(buffer >> 6)]);
        output.append(PAD);

      }
      else {

        buffer = (eightbit(source[i]) << 16) | (eightbit(source[i + 1]) << 8) | (eightbit(source[i + 2]));
        output.append(BASE64CHARS[sixbit(buffer >> 18)]);
        output.append(BASE64CHARS[sixbit(buffer >> 12)]);
        output.append(BASE64CHARS[sixbit(buffer >> 6)]);
        output.append(BASE64CHARS[sixbit(buffer)]);

      }

    }

    return output.toString();

  }

  public static String decode(String str) {

    StringBuffer output = new StringBuffer();
    byte[] source = str.getBytes();
    int data = 0,
        buffer = 0,
        bytesRead = 0;

    for(int i = 0; i < source.length; i++) {

      if(bytesRead < 4) {

        data = decodeInt(source[i]);

        if(data == -1)
          continue;
        else if(data == -2 && bytesRead != 2 && bytesRead != 3)
          return null;
        else if(data == -2 && bytesRead == 2) {

          output.append((char) eightbit(buffer >> 4));

          return output.toString();

        }
        else if(data == -2 && bytesRead == 3) {

          output.append((char) eightbit(buffer >> 10));
          output.append((char) eightbit(buffer >> 2));

          return output.toString();

        }
        else {

          buffer = (buffer << 6) | sixbit(data);
          bytesRead++;

        }

      }
      if(bytesRead == 4) {

        output.append((char) eightbit(buffer >> 16));
        output.append((char) eightbit(buffer >> 8));
        output.append((char) eightbit(buffer));

        buffer = 0;
        bytesRead = 0;

      }

    }

    return output.toString();

  }

  private static int decodeInt(int input) {

    if (input >= 'A' && input <= 'Z')
      return input - 'A';
    if (input >= 'a' && input <= 'z')
      return input - 'a' + 26;
    if (input >= '0' && input <= '9')
      return input - '0' + 52;
    if (input == '+')
      return 62;
    if (input == '/')
      return 63;
    if (input == '=')
      return -2;

    return -1;

  }


  private static int sixbit(int input) {

    return (input & 0x3F);

  }

  private static int eightbit(int input) {

    return (input & 0xFF);

  }

}
